# Deskriptivní statistiky z tabulky dat
Představte si, že jsme provedli průzkum spokojenosti zákazníků s novou službou, kde zákazníci hodnotili spokojenost na škále od 1 (velmi nespokojený) do 10 (velmi spokojený). Zde je souhrn dat z odpovědí 200 náhodně vybraných zákazníků:
```{ojs}
//| echo: false
// Definice dat: 200 uživatelů, zašikmeno k vyšším hodnotám (1-10)
data_base = [
{x: 1, n: 2},
{x: 2, n: 3},
{x: 3, n: 5},
{x: 4, n: 10},
{x: 5, n: 15},
{x: 6, n: 25},
{x: 7, n: 30},
{x: 8, n: 40},
{x: 9, n: 45},
{x: 10, n: 25}
]
```
```{ojs}
//| echo: false
//| label: tbl-spokojenost-kumulativni
//| tbl-cap: "Tabulka četností s kumulativní relativní četností (n=200)"
// Výpočet relativních a kumulativních četností
table_final = {
let acc = 0;
return data_base.map(d => {
const f = d.n / 200;
acc += f;
return {
x: d.x,
n: d.n,
f: f.toFixed(3),
F: acc.toFixed(3)
};
});
}
// Vykreslení tabulky
Inputs.table(table_final, {
columns: ["x", "n", "f", "F"],
header: {
x: "Hodnocení (xᵢ)",
n: "Četnost (nᵢ)",
f: "Relat. četnost (pᵢ)",
F: "Kumul. relat. četnost (Fᵢ)"
}
})
```
Pojďme tato data zobrazit graficky v podobně histogramu, tedy diagramu četností
```{ojs}
//| echo: false
// Histogram
Plot.plot({
height: 250,
//x: {label: "Hodnocení (xᵢ)", domain: [0.5, 10.5], ticks: d3.range(1, 11)},
//y: {label: "Četnost (nᵢ)", grid: true},
//marks: [
// Plot.barY(data_base, {x: "x", y: "n", fill: "#0275d8", inset: 0.5}),
// Plot.ruleY([0])
//]
marks: [
Plot.barY(data_base, {
x: "x",
y: "n",
fill: "steelblue",
inset: 0.5
}),
Plot.ruleY([0])
]
})
```
## Ukazatele polohy: kvantily
**Kvantil** je **hodnota, pod kterou leží určité procento dat**. Důležité kvantily mají svá spěcifická označení: **medián** pro polovinu, **kvartily** pro čtvrtiny, **decily** pro desetiny a **percentily** pro setiny. Proto platí že např medián = druhý kvartil = pátý deci = padesátý percentil.
**Min** je nejmenší, **max** největší hodnota v datech.
Pokud budeme hledat kvantily v tabulce, zaměříme se na sloupec *Kumulativní (Fᵢ)* ve kterém hledáme, kdy poprvé dosáhneme dané hodnoty.
1. **Dolní kvartil ($Q_1$):** hledáme hodnotu **0,25**. V tabulce vidíme, že pro $x=5$ je $F(5)=0,175$ menší než $0.5$, ale $F(6)=0,300$. Takže $Q_1 = 6$.
2. **Medián ($Q_2$):** stručněji: $F(7)= 0,450, F(8)=0.650$ Protože $F(7) < 0.5 < F(8)$, je $Q2=8$.
3. **Horní kvartil ($Q_3$):** $F(8)=0.650, F(9)=0.875 \longrightarrow Q_3 = 9$.
4. **$Q_3$ pomocí interpolací:** $k=1 + (10-1)\cdot 0.75 = 7.75, F(8)<7.75<F(9)\longrightarrow Q_3= 8+(8-7) \cdot 0.75=8.75$
Různé způsoby výpočtu percentilů mohou vést k různým, ale podobným výsledkům.
## Kvantilové ukazatele variability: jak moc se zákazníci (ne)shodují?
Když známe tyto body, můžeme popsat, jak moc jsou data "rozptýlená":
4. **Rozpětí** (*range*): rozdíl mezi nejlepším a nejhorším $max-min$, v našich datech $range = 10 - 1 = 9$.
5. **Mezikvartilové rozpětí** (*inter-quartile-range, IQR*): rozdíl mezi třemi čtvrtinami a jednou čtvrtinou, $Q_3-Q_1$, tedy $IQR = 9 - 6 = 3$.
::: {.callout-note}
**Range** ani min,max se nemusí shodovat z rozsahem škály. Například škála 1-10 může mít v datech min=5 a max=7.
**IQR** nám říká, v jakém rozmezí se pohybují hodnoty "střední poloviny", tedy když ignorujeme čtvrtinu nejmenších a čtvrtinu největších hodnot.
:::
## Aritmetický průměr a těžiště dat
Nejznámější hodnotou popisující soubor dat je bezesporu průměr, přesněji artimetický průměr. Ten lze vypočít tak, že sečteme všechny hodnoty a vydělíme je počtem hodnot, $\bar{x} = \frac{\sum (x_i)}{N}$. Pokud máme data v tabulce, pak tam některé hodnoty máme vícekrát, např u nás
$\bar{x} = \frac{1+1+2+2+2+3+\dots}{200}$, což můžeme lehce upravit pomocí součinu na
$\bar{x} = \frac{1\cdot 2+2\cdot 3+3\cdot 5+\dots}{200}$, velký zlomek ještě rozdělit na samostatné zlomky
$\bar{x} = \frac{1\cdot 2}{200}+\frac{2\cdot 3}{200}+\frac{3\cdot 5}{200}+\dots$ a na závěr ještě upravit na relativní četnosti
$\bar{x} = 1\cdot \frac{2}{200}+2\cdot \frac{3}{200}+3\cdot\frac{5}{200}+\dots$
Získali jsme tak vzorec který je uváděný jako **vážený průměr**, kde každou hodnotu "vážíme" její četností $n_i$ nebo relativní četností $p_i$:
$$\bar{x} = \frac{\sum x_i\cdot n_i}{N}=\sum x_i\cdot \frac{n_i}{N}=\sum x_i\cdot p_i$$
Pro naše data pomocí četností
$$ \bar{x} =\frac{\sum x_i\cdot n_i}{N} = \frac{1\cdot 2 + 2\cdot 3 + 3\cdot 5 + \dots }{200} = 7.56 $$
nebo pomocí relativních četností
$$\bar{x} =\sum x_i\cdot p_i = 1\cdot 0.010 + 2\cdot 0.025 + 3\cdot 0.050 + \dots = 7.56$$
* **Průměr** se chová jako **těžiště**. Ti nespokojení zákazníci na levé straně (známky 1, 2, 3) fungují jako závaží na dlouhém rameni páky. I když je jich málo, svou „vzdáleností“ od středu táhnou průměr doleva.
```{ojs}
//| echo: false
//| label: fig-tiziste-oprava
// 1. Spočítáme průměr přímo zde, aby byla buňka soběstačná
mean_vypocet = d3.sum(data_base, d => d.x * (d.n / 200))
// 2. Vizualizace páky
Plot.plot({
height: 100,
width: 600,
marginLeft: 50,
x: { domain: [1, 10], grid: false },
y: { axis: null, domain: [0, 0.4] },
marks: [
// Vodorovná osa páky (deska)
Plot.ruleY([0.1], {x1: 1, x2: 10, strokeWidth: 2, stroke: "#666"}),
// Závaží (body) - jejich plocha odpovídá počtu lidí
Plot.dot(data_base, {
x: "x",
y: 0.1,
r: d => d.n / 10,
fill: d => d.x > mean_vypocet ? "#4e79a7" : "#e15759", // modrá vpravo, červená vlevo od průměru
stroke: "white"
}),
// Těžiště (Podstavec / Fulcrum)
Plot.areaY([
{x: mean_vypocet - 0.2, y: 0},
{x: mean_vypocet, y: 0.1},
{x: mean_vypocet + 0.2, y: 0}
], {
x: "x",
y: "y",
fill: "#666"
}),
// Text s hodnotou průměru
Plot.text([mean_vypocet], {
x: mean_vypocet,
y: 0.1,
text: d => `x̄ = ${d.toFixed(2)}`,
dy: 25,
fontWeight: "bold",
fill: "#666"
})
]
})
```
## variabilita odvozená z průměru
připomeňme, že rozptyl z dat získáme pomocí vzorce
$$\sigma^2 = \frac{\sum (x_i - \bar{x})^2}{n}$$
pokud budeme mt data vážená vektorem relativních četností $\mathbf{p}$, pak
$$
\sigma^2 = \sum (x_i - \bar{x})^2\cdot p_i
$$
```{ojs}
//| echo: false
// 1. Konstanty a ovládací prvky
source_n = 200
viewof extra_x = Inputs.range([1, 10], {step: 1, value: 1, label: "Poloha extra bodu (x)"})
viewof extra_n = Inputs.range([0, 20], {step: 1, value: 5, label: "Počet přidaných osob"})
// 2. Výpočty statistik
dynamic_data = [
...data_base,
{x: extra_x, n: extra_n}
]
total_n = source_n + extra_n
current_mean = (d3.sum(data_base, d => d.x * d.n) + (extra_x * extra_n)) / total_n
current_median = {
const sorted = dynamic_data.flatMap(d => Array(Math.round(d.n)).fill(d.x)).sort((a, b) => a - b);
return d3.median(sorted);
}
// 3. Vizualizace
Plot.plot({
height: 100,
width: 600,
marginLeft: 50,
x: { domain: [1, 10], grid: false },
y: { axis: null, domain: [0, 0.4] },
marks: [
// Vodorovná osa páky
Plot.ruleY([0.1], {x1: 1, x2: 10, strokeWidth: 4, stroke: "#666"}),
// VRSTVA 1: Původní data (VELIKOST JEN Z data_base)
Plot.dot(data_base, {
x: "x",
y: 0.1,
r: d => d.n / 5,
fill: d => d.x > current_mean ? "#4e79a7" : "#e15759",
fillOpacity: 0.8
}),
// VRSTVA 2: Extra bod (POSUNUTÝ NAHORU A DYNAMICKÝ)
// y: 0.2 ho zvedne nad úroveň původních teček
Plot.dot([{x: extra_x, n: extra_n}], {
x: "x",
y: 0.2,
r: extra_n/2 ,
fill: "#666",
fillOpacity: 1,
stroke: "white",
strokeWidth: 2
}),
// Svislá čára pro MEDIÁN
Plot.ruleX([current_median], {stroke: "#666", strokeWidth: 2, strokeDasharray: "4,4"}),
Plot.text([current_median], {x: current_median, y: -0.2, text: d => `Medián: ${d}`, dy: -10, fill: "#666", fontWeight: "bold"}),
// Těžiště (Průměr)
Plot.areaY([
{x: current_mean - 0.2, y: 0},
{x: current_mean, y: 0.1},
{x: current_mean + 0.2, y: 0}
], { x: "x", y: "y", fill: "#666" }),
Plot.text([current_mean], {
x: current_mean, y: 0.08,
text: d => `x̄ = ${d.toFixed(2)}`,
dy: 20, fill: "#666", fontWeight: "bold"
})
]
})
```